module vx

import dl

import v.table
import vcp.mlog

// T.typ 需要 get_T_typ.patch

// hoho, sum type inner express
pub struct SumTypeInner {
    pub mut:
    ptr voidptr
    typ int
}

//
pub fn sum_type_data(valp voidptr) voidptr {
    mut val := &SumTypeInner(valp)
    return val.ptr
}
pub fn sum_type_type(valp voidptr) int {
    mut val := &SumTypeInner(valp)
    val = valp
    return val.typ
}

// hoho
pub struct SliceHeader {
    pub mut:
    data voidptr
    len int
}

// hoho
pub struct StringHeader {
    pub mut:
    data voidptr
    len int
}

// hoho
pub struct ArrayHeader {
    pub mut:
    data voidptr
    len int
    elemsize int
}

// hoho
pub struct MapHeader {
    pub mut:
    data voidptr
    len int
    elemsize int
}

// hoho
pub struct InterfaceHeader {
    pub mut:
    data voidptr
}

/////////////
struct Vars {
    mut:
    vtab &table.Table = 0
    types map[string]Type // name =>
    // types &Types = 0

    ffidlh voidptr
    ffi_call_fnptr voidptr
    ffi_prepare_fnptr voidptr
}

const varsro = &Vars{}

fn init() {
    mut vars := varsro
    vars.vtab = table.new_table()

}

/////////////
// table.Kind

struct TypeImpl {
    mut:
    kind table.Kind// TypeKind
    itype int = -1
    name string
    size int
    modname string

    // for ptr
    under &TypeImpl = vnil

    // for struct
    fieldcnt int
    fieldtys []&TypeImpl
    fieldinfos []FieldData2

    // for func
    paramcnt int
    paramtys []&TypeImpl
    retcnt int
    rettys []&TypeImpl
    thisty &TypeImpl = vnil

    // for chan
    chty &TypeImpl = vnil
    chcap int
    chdir int

    // interface
    functys []&TypeImpl
}
struct TypeFieldStruct{
    mut:
    name string
}
struct TypeFieldFunc{
    mut:
    name string
}

pub struct Type {
    mut:
    tyref &TypeImpl = vnil
    tysym &table.TypeSymbol = vnil
}

/////
pub fn (t Type) name() string { return t.tyref.name }
pub fn (t Type) kind2() table.Kind { return t.tysym.kind }
pub fn (t Type) len() int { return t.tyref.size }
pub fn (t Type) typ() int { return t.tyref.itype }
pub fn (t Type) num_fields() int {
    info := t.tysym.info
    mut num := -1
    match info {
        table.Struct { num = info.fields.len }
        else{}
    }
    return num
}
pub fn (t Type) num_args() int {
    info := t.tysym.info
    mut num := -1
    match info {
        table.FnType { num = info.func.params.len }
        else{}
    }
    return num
}
pub fn (t Type) num_methods() int {
    mut num := -1
    if t.tysym.kind == table.Kind.interface_ {
        num = t.tysym.methods.len
    }else if t.tysym.kind == table.Kind.struct_ {
        num = t.tysym.methods.len
    }
    return num
}
pub fn (t Type) has_return() bool {
    info := t.tysym.info
    mut num := false
    match info {
        table.FnType { num = info.func.return_type != 0 }
        else{}
    }
    return num
}
fn (t Type) new() Value { return Value{} }
fn (t Type) convertable_to(t2 Type) bool { return false }
fn (t Type) assignable_to(t2 Type) bool { return false }
pub fn (t Type) str() string {
    if t.tysym == vnil && t.tyref == vnil {
        return 'Type{&nil' + ", &nil}"
    }
    return 'Type{' + t.tysym.debug().str() + ", $t.tyref}"
}

/////

pub union Variant {
    pub mut:
    ival   int
    i64val i64
    u64val u64
    fval f32
    dval f64
    strval string
    boolval bool
    ptrval voidptr
    recordval voidptr
}
pub struct Value {
    pub mut:
    typ Type
    val Variant
}

pub struct Method {
    pub mut:
    receiver Value
}

// TODO
// the <T>, gen/comptime.v
struct TmplType {
    mut:
    typ string // ???
    name string
    // methods []FunctionData //
    fields []FieldData // from comptime internal

}

struct MethodArgs2 {
    mut:
    ma MethodArgs

    // expand fields
    typ int
    idx int

    // my extra
    styp string
}

struct FunctionData2 {
    mut:
    fd FunctionData

    // expand fields
    name string
    attrs []string
    args []MethodArgs2
    return_type int = -1
    typ int = -1

    // my extra
    return_stype string
    styp string
}

// same v compiler FieldData
struct FieldData2 {
    pub mut:
    fd FieldData
    stype string
}

struct TypeIndex {
    mut:
    int_ int
    rune_ rune
    bool_ bool
    byte_ byte
    string_ string
    u32_ u32
    i64_ i64
    u64_ u64
    i8_ i8
    i16_ i16
    u16_ u16
    f32_ f32
    f64_ f64
    size_t_ size_t
    voidptr_ voidptr
    charptr_ charptr
    byteptr_ byteptr

    test123_ FieldData
}

fn (this TypeIndex) foo(x int) {
}

// register to idx
fn register_type_symbol(idx int, sym table.TypeSymbol) int {
    mut vars := varsro
    mut vtab := vars.vtab
    mut dummy := table.TypeSymbol{}
    mut needset := true
    for vtab.types.len < idx {
        vtab.types << dummy
        needset = false
    }
    if needset {
        vtab.types[idx] = sym
        vtab.type_idxs[sym.name] = idx
    } else {
        newidx := vtab.register_type_symbol(sym)
        assert newidx == idx
    }
    return idx
}

// like go reflect.TypeOf()
// but not work
// usage: typeof_obj(someobj)
// not for: func
// typeof_object
fn typeofo<T>(v T) Type {
    mut vars := varsro
    mut vtab := vars.vtab

    tystr := T.name
    if tystr in vars.types {
        return vars.types[tystr]
    }

    tysz := int(sizeof(v))
    mut typeid := vtab.find_type_idx(tystr)
    if typeid == 0 {
        typeid = T.typ
        // infoln(@LINE, tystr, typeid)
        tydesc := "$v"
        bestruct := type_is_struct(tystr, tydesc)
        befunc := type_is_function(tystr, tystr)
        beitf := type_is_interface(tystr, tydesc)
        infoln(@LINE, bestruct, befunc, beitf, tydesc)
        if bestruct {
            mut fields2 := []table.Field{}
            mut f3 := table.Field{}
            mut fields := []FieldData2{} // this struct come from v compiler core
            mut f2 := FieldData2{}
            mut fty := ""
            mut ftyid := 0
            $for f in T.fields {
                f2.fd = f
                fty = f.name.trim_right('_')
                f2.stype = fty
                // println("$f2, $f.name $fty")
                fields << f2
                //fty = vtab.get_type_name(f.typ)
                f3 = table.Field{name: f.name}
                f3.typ = f.typ
                // infoln(@LINE, fty, f3.name, f.typ, )
                fields2 << f3
            }
            // infoln(@LINE, fields.len)
            mut tysym := &table.TypeSymbol{}
            tysym.kind = table.Kind.struct_
            tysym.name = tystr
            tysym.info = table.Struct{fields: fields2}
            // vtab.register_type_symbol(tysym)
            mut x := vtab.find_type_idx(tystr)
            if x == 0 {
                x = register_type_symbol(typeid, tysym)
            }
            infoln(@LINE, x, typeid)
            mut tyref := &TypeImpl{}
            tyref.kind = .struct_
            tyref.name = tystr
            tyref.size = tysz
            tyref.itype = typeid
            tyobj := Type{tyref, tysym}
            vars.types[tystr] = tyobj
            return tyobj
        }
    }else{
        mut tysym := vtab.get_type_symbol(typeid)
        infoln(@LINE, tystr, typeid, tysym.debug())
        mut tyref := &TypeImpl{}
        tyref.itype = typeid
        tyref.size = tysz
        tyref.name = tystr
        tyobj := Type{tyref, tysym}
        vars.types[tystr] = tyobj
        return tyobj
    }
    return Type{}
}
// use for have not an instance of T
// usage: typeof_type<sometype>()
// typeof_type
fn typeoft<T>() Type {
    v := T{}
    return typeofo<T>(v)
}

// TODO 合并到 typeofo
fn typeof_func(tystr string) Type {
    infoln(@LINE, tystr)
    mut vars := varsro
    mut vtab := vars.vtab

    {
        typidx := vtab.find_type_idx(tystr)
        if typidx == 0 {
            infoln(@LINE, tystr, typidx)
        }
    }

    lpar := tystr.index('(') or { panic("not func $tystr") }
    rpar := tystr.last_index(')') or { panic("not func $tystr") }
    infoln(@LINE, lpar, rpar)

    mut fd2 := FunctionData2{}
    mut fd := FunctionData{}
    fd2.styp = tystr[rpar+1..].trim_space()

    tylst := tystr[lpar+1..rpar].split(", ")
    infoln(@LINE, tylst)

    mut prms := []table.Param{}
    for i := 0; i < tylst.len; i++ {
        prmtystr := tylst[i]
        mut typidx := vtab.find_type_idx(prmtystr)
        if typidx == 0{
            infoln(@LINE, prmtystr, typidx)
        }
        mut prm := table.Param{typ: typidx, name: "a$i"}
        prms << prm
    }

    retystr := fd2.styp
    mut typidx := vtab.find_type_idx(retystr)
    if typidx == 0 {
        infoln(@LINE, retystr, tystr)
    }

    mut fno := table.Fn{params: prms, return_type: typidx}
    fno.name = tystr
    mut fnty := table.FnType{func: fno }
    mut tysym := &table.TypeSymbol{}
    tysym.name = tystr
    tysym.info = fnty
    tysym.kind = table.Kind.function

    mut tyref := &TypeImpl{}
    tyref.name = tystr
    tyref.itype = -1
    tyref.kind = table.Kind.function

    mut tyo := Type{tyref, tysym}
    vars.types[tystr] = tyo
    return tyo
}


fn typeof_itf<T>() Type {
    // println(T.name)
    // println(T.typ)

    mut vars := varsro
    mut vtab := vars.vtab

    tystr := T.name
    itype := T.typ
    mut fns := []table.Fn{}
    mut fn0 := table.Fn{}
    mut prms := []table.Param{}
    mut prm := table.Param{}
    $for m in T.methods {
        // println(m)
        for idx, arg in m.args {
            infoln(@LINE, arg.str(), arg.typ)
            prm = table.Param{typ: arg.typ, name: "a$idx" }
            prms << prm
        }
        fn0 = table.Fn{return_type: m.return_type, params: prms }
        fn0.name = m.name
        fns << fn0
    }
    //infoln(@LINE, fns.str())

    mut tysym := &table.TypeSymbol{}
    tysym.methods = fns
    tysym.name = tystr
    tysym.kind = table.Kind.interface_
    tysym.info = table.Interface{}

    mut tyref := &TypeImpl{}
    tyref.name = tystr
    tyref.itype = itype
    tyref.size = fns.len * int(sizeof(voidptr))

    mut tyo := Type{tyref, tysym}
    vars.types[tystr] = tyo
    return tyo
}

fn infoln(line string, args ... Any) {
    mlog.info(@FILE, line, args...)
}

pub fn strhaveall(s, sub string) bool {
    for c in sub {
        if ! (s.index_byte(c) >= 0) {
            return false
        }
    }
    return true
}

const curfile = @FILE
const curmod = @MOD

// tyname T.name
// desc "${obj}"
fn type_is_struct(tyname string, desc string) bool {
    if tyname.contains(".") && strhaveall(desc, ".:{}") && desc.ends_with("}") {
        pos := desc.index('{') or {-1}
        sub := desc[..pos+1] + "..." + desc[desc.len-1..]
        infoln(@LINE, "maybe struct ${tyname}, ${sub}")
        return true
    }
    return false
}
fn type_is_interface(tyname string, desc string) bool {
    if strhaveall(desc, "{}") &&
        !desc.contains(".") && desc.ends_with("}") {
        pos := desc.index('{') or {-1}
        sub := desc[..pos+1] + "..." + desc[desc.len-1..]
        infoln(@LINE, "maybe interface ${tyname}, ${sub}")
        return true
    }
    return false
}

fn type_is_function(tyname string, desc string) bool {
    mlog.info(@FILE, @LINE, "$tyname fff $desc")
    return false
}

/////
fn valueofo<T>(v T) Value {
    return Value{}
}

fn valueof_func(tystr string, f voidptr) Value {
    typ := typeof_func(tystr)
    mut val := Variant{}
    val.ptrval = f
    return Value{typ, val}
}

// 使用libdl获取ffi_call，并调用，减少编译依赖
fn (v Value) call(args []Value) []Value {
    mut res := []Value{}

    return res
}

fn init_dlffi() {
    mut vars := varsro
    if vars.ffidlh != vnil {
        return
    }

    ffiso := "/usr/lib/libffi.so"
    h := dl.open(ffiso, dl.rtld_now)
    if h == vnil {
        panic("cannot open $ffiso")
    }

    vars.ffidlh = h
    mut ptr := dl.sym(h, "ffi_prepare")
    assert ptr != vnil
    vars.ffi_prepare_fnptr = ptr
    ptr = dl.sym(h, "ffi_call")
    assert ptr != vnil
    vars.ffi_call_fnptr = ptr
}

fn cdeclimp(sofile string, fun string) voidptr {
    return vnil
}
